﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;

namespace Dapper_Core.Basic
{
    public sealed class Util
    {
        /// <summary>
        /// banjian 2016-3-9
        /// 获取字段名对应的属性，要求字段与属性名一致（只有大小写不一致）
        /// </summary>
        /// <param name="type"></param>
        /// <param name="fi"></param>
        /// <returns></returns>
        public static PropertyInfo GetPropertyInfoByFieldInfo(Type type, FieldInfo fi)
        {
            return type.GetProperty(fi.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        }

        /// <summary>
        /// banjian 2016-3-30
        /// 根据列表类型创建出空的泛型列表
        /// </summary>
        /// <param name="fullName"></param>
        /// <returns></returns>
        public static object GetGenericListByType(string fullName)
        {
            var obj = Activator.CreateInstance(Type.GetType(fullName));
            return (obj);
        }

        public static IList<string> GetTablefields(Type dtoType)
        {
            IList<string> result = new List<string>();
            FieldInfo[] fields = dtoType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
            foreach (FieldInfo fi in fields)
            {
                Type t = fi.FieldType;
                if ((t.IsValueType && !t.IsEnum) || !(t.IsSubclassOf(typeof(BaseModel)) || t.IsGenericType || t == typeof(IList) || fi.Name.Equals("innerSearchLevel") || fi.Name.Equals("status")))
                {
                    result.Add(fi.Name);
                }
            }
            return result;
        }

        public static IList<string> GetTheDTOfields(Type dtoType)
        {
            IList<string> result = new List<string>();
            FieldInfo[] fields = dtoType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
            foreach (FieldInfo fi in fields)
            {
                Type t = fi.FieldType;
                PropertyInfo pi = GetPropertyInfoByFieldInfo(dtoType, fi);
                if ((pi != null && pi.PropertyType.IsSubclassOf(typeof(BaseModel))) || t.IsSubclassOf(typeof(BaseModel)))
                {
                    result.Add(fi.Name);
                }
            }
            return result;
        }

        /// <summary>
        /// 根据指定的dtotype获取该dto中的字段名称和对应的字段类型
        /// </summary>
        /// <param name="dtoType"></param>
        /// <returns></returns>
        public static Dictionary<string, Type> GetTableFieldAndTypes(Type dtoType)
        {
            Dictionary<string, Type> result = new Dictionary<string, Type>();
            FieldInfo[] fields = dtoType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
            foreach (FieldInfo fi in fields)
            {
                Type t = fi.FieldType;
                if ((t.IsValueType && !t.IsEnum) || !(t.IsSubclassOf(typeof(BaseModel)) || t.IsGenericType || t == typeof(IList) || fi.Name.Equals("innerSearchLevel") || fi.Name.Equals("status")))
                {
                    result.Add(fi.Name, t);
                }
            }
            return result;
        }

        /// <summary>
        /// 递归处理：把dto填充到uftable中的DataTable中的DataColumnCollection和DataRow
        /// </summary>
        /// <param name="columnSets"></param>
        /// <param name="dcc"></param>
        /// <param name="dr"></param>
        /// <param name="dto"></param>
        /// <param name="parentfieldName"></param>
        /// <param name="level"></param>
        /// <param name="standLevel"></param>
        public static void RescueSetDTOWithColumnType(Hashtable columnSets, DataColumnCollection dcc, DataRow dr, BaseModel dto, string parentfieldName, int level, int standLevel)
        {
            if (level >= standLevel) return;
            //IList<string> tableFields = Util.GetTablefields(dto.GetType());
            Dictionary<string, Type> tableFields = Util.GetTableFieldAndTypes(dto.GetType());
            IList<string> dtoFields = Util.GetTheDTOfields(dto.GetType());
            IList<string> freeItemFields = new List<string>();
            if (!columnSets.Contains(parentfieldName))
            {
                foreach (string tf in tableFields.Keys)
                {
                    dcc.Add(parentfieldName + "_" + tf, tableFields[tf]);
                }

                //自由项支持
                for (int i = 0; i < 10; i++)
                {
                    freeItemFields.Add("freeitem" + i);
                    dcc.Add(parentfieldName + "_" + "freeitem" + i);
                }

                columnSets.Add(parentfieldName, parentfieldName);
            }

            foreach (string tf in tableFields.Keys)
            {
                var v = dto.GetFieldValue(tf);
                if (v == null)
                {
                    dr[parentfieldName + "_" + tf] = DBNull.Value;
                }
                else
                {
                    dr[parentfieldName + "_" + tf] = v;
                }
            }
            foreach (string tf in freeItemFields)
            {
                dr[parentfieldName + "_" + tf] = dto.GetFieldValue(tf);
            }

            foreach (string tf in dtoFields)
            {
                object obj = dto.GetFieldValue(tf);
                if (obj != null)
                {
                    if (obj is BaseModel)
                    {
                        BaseModel joinDTO = (BaseModel)obj;
                        int myLevel = level + 1;
                        RescueSetDTOWithColumnType(columnSets, dcc, dr, joinDTO, tf, myLevel, standLevel);
                    }
                }
            }
        }

        public static bool ContainsDTO(DataRow dr, DataColumnCollection dcc, string fieldHead)
        {
            bool contain = false;
            string fstr = fieldHead + "_id";
            if (dcc.Contains(fstr))
            {
                object obj = dr[fstr];
                if (obj != null && obj != DBNull.Value)
                {
                    return true;
                }
            }
            return contain;
        }

        public static string ConvertBytesToHex(byte[] arrByte, bool reverse)
        {
            if (arrByte == null) return string.Empty;
            StringBuilder sb = new StringBuilder();
            if (reverse)
                Array.Reverse(arrByte);
            foreach (byte b in arrByte)
                sb.AppendFormat("{0:x2}", b);
            return sb.ToString();
        }

        public static byte[] ConvertHexToBytes(string value)
        {
            int len = value.Length / 2;
            byte[] ret = new byte[len];
            for (int i = 0; i < len; i++)
                ret[i] = (byte)Convert.ToInt32(value.Substring(i * 2, 2), 16);
            return ret;
        }

        /// <summary>
        /// 根据属性类型获取值,用于从DataRow获取记录值并赋给字段
        /// </summary>
        /// <param name="t"></param>
        /// <param name="oValue"></param>
        /// <returns></returns>
        public static object GetVOPropertyValueByType(Type t, string fieldName, object oValue)
        {
            string typename = t.Name;
            if (typename == "String")
            {
                if (oValue == null || oValue == DBNull.Value)
                    return null;
                return oValue.ToString();
            }
            #region 能为null的value类型
            if (t.IsValueType && t.IsGenericType)
            {
                if (oValue == null || oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString().Trim()))
                {
                    return null;
                }
                if (t == typeof(System.Nullable<Decimal>))
                {
                    try
                    {
                        return System.Convert.ToDecimal(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
                else if (t == typeof(System.Nullable<DateTime>))
                {
                    try
                    {
                        return DateTime.Parse(oValue.ToString());
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
                else if (t == typeof(System.Nullable<Int32>))
                {
                    try
                    {
                        return System.Convert.ToInt32(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
                else if (t == typeof(System.Nullable<byte>))
                {
                    try
                    {
                        return System.Convert.ToByte(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
                else if (t == typeof(System.Nullable<Int16>))
                {
                    try
                    {
                        return System.Convert.ToInt16(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
                else if (t == typeof(System.Nullable<double>))
                {
                    try
                    {
                        return System.Convert.ToDouble(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
                else if (t == typeof(System.Nullable<Int64>))
                {
                    try
                    {
                        return System.Convert.ToInt64(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
                else if (t == typeof(System.Nullable<long>))
                {
                    try
                    {
                        return System.Convert.ToInt64(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
            }
            #endregion
            if (typename == "Decimal")
            {
                if (oValue == null || oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString().Trim()))
                {
                    return 0M;
                }
                return System.Convert.ToDecimal(oValue);
            }
            if (typename == "DateTime")
            {
                if (oValue == null || oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString().Trim()))
                {
                    return DateTime.MinValue;
                }
                if (oValue is DateTime) return oValue;
                DateTime d = DateTime.Parse(oValue.ToString());
                return d;
            }
            else if (typename == "Boolean")
            {
                try
                {
                    if (oValue == null || oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString().Trim()))
                        return false;
                    else
                    {
                        if (oValue.ToString().Trim().Equals("1") || oValue.ToString().Trim().ToLower().Equals("true"))
                            return true;
                        else
                            return false;
                    }
                }
                catch (Exception e)
                {
                    return false;
                }
            }
            else if (typename == "Int32")
            {
                if (oValue == null || oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString().Trim()))
                {
                    return 0;
                }
                return System.Convert.ToInt32(oValue);
            }
            else if (t.IsEnum)
            {
                if (oValue == null)
                    return null;
                else
                    return Enum.Parse(t, oValue.ToString(), true);
            }
            else if (typename == "Byte")
            {
                if (oValue == null || oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString().Trim()))
                {
                    return (byte)0;
                }
                return System.Convert.ToByte(oValue);
            }
            else if (typename == "Double")
            {
                if (oValue == null || oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString().Trim()))
                {
                    return 0d;
                }

                return System.Convert.ToDouble(oValue);
            }
            else if (typename == "Int64")
            {
                if (oValue == null || oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString().Trim()))
                {
                    return 0;
                }
                return System.Convert.ToInt64(oValue);
            }
            #region 能为null的value类型
            if (t.IsValueType && t.IsGenericType)
            {
                if (oValue == null || oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString().Trim()))
                {
                    return null;
                }
                if (t == typeof(System.Nullable<Decimal>))
                {
                    try
                    {
                        return System.Convert.ToDecimal(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
                else if (t == typeof(System.Nullable<Int32>))
                {
                    try
                    {
                        return System.Convert.ToInt32(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
                else if (t == typeof(System.Nullable<double>))
                {
                    try
                    {
                        return System.Convert.ToDouble(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }

                else if (t == typeof(System.Nullable<Int64>))
                {
                    try
                    {
                        return System.Convert.ToInt64(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
                else if (t == typeof(System.Nullable<Int16>))
                {
                    try
                    {
                        return System.Convert.ToInt16(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
                else if (t == typeof(System.Nullable<long>))
                {
                    try
                    {
                        return System.Convert.ToInt64(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
                else if (t == typeof(System.Nullable<byte>))
                {
                    try
                    {
                        return System.Convert.ToByte(oValue);
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
                else if (t == typeof(System.Nullable<DateTime>))
                {
                    try
                    {
                        return DateTime.Parse(oValue.ToString());
                    }
                    catch (Exception e)
                    {
                        return null;
                    }
                }
            }
            #endregion
            return oValue;

        }

        private static T ConvertTo<T>(object oValue)
        {
            T v;

            if (oValue == null && typeof(T).IsValueType)
            {
                v = default(T);
            }
            else
            {
                if (typeof(T) == typeof(bool) && oValue is string)
                {
                    bool bv = false;
                    //bool 支持 字符串“1” 和 “0” 转换
                    if (!string.IsNullOrWhiteSpace((string)oValue))
                    {
                        if (oValue.ToString().Trim().Equals("1"))
                        {
                            bv = true;
                        }
                    }

                    oValue = bv;
                }

                v = (T)Convert.ChangeType(oValue, typeof(T), Thread.CurrentThread.CurrentCulture);
            }

            return v;
        }
        
        public static void CopyValues(BaseModel from, BaseModel to)
        {
            if (from == null || to == null) return;

            PropertyInfo[] fields = to.GetType().GetProperties();
            foreach (PropertyInfo fi in fields)
            {
                to.SetFieldValue(fi.Name, from.GetFieldValue(fi.Name));
            }
        }

        /*深层复制对象，内存序列化复制*/
        public static object deepCopyBySerlizer(object obj)
        {
            System.IO.MemoryStream buffer = null;
            BinaryFormatter formatter = new BinaryFormatter();
            try
            {
                buffer = new System.IO.MemoryStream();
                formatter.Serialize(buffer, obj);
                buffer = new System.IO.MemoryStream(buffer.ToArray());
                object returnObj = formatter.Deserialize(buffer);
                return returnObj;
            }
            finally
            {
                if (buffer != null)
                    buffer.Close();
                formatter = null;
            }
        }

        /*深层复制对象，字段复制，不支持基类私有字段的复制 */
        public static object deepCopyByFields(object obj)
        {
            //建立指定类型的一个实例  
            object newObject = Activator.CreateInstance(obj.GetType());

            FieldInfo[] fields = newObject.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
            foreach (FieldInfo fi in fields)
            {
                Type ICloneType = fi.FieldType.GetInterface("ICloneable", true);
                if (ICloneType != null)
                {
                    //取得对象的Icloneable接口
                    ICloneable IClone = (ICloneable)fi.GetValue(obj);
                    if (IClone != null)
                    {
                        fi.SetValue(newObject, IClone.Clone());
                    }
                    else
                    {
                        fi.SetValue(newObject, fi.GetValue(obj));
                    }
                }
                else
                {
                    //如果该字段不支持Icloneable接口，直接设置即可。                 
                    fi.SetValue(newObject, fi.GetValue(obj));
                }
            }
            return newObject;
        }

        #region 性能优化
        public static int ContainsColumn(Hashtable ht, string fieldHead)
        {
            int index = -1;
            string fstr = fieldHead;
            if (ht.ContainsKey(fstr))
            {
                index = Convert.ToInt32(ht[fstr]);
            }
            return index;
        }

        /// <summary>
        /// 返回fieldHead + "_id"在dr中的列的索引。
        /// </summary>
        /// <param name="dr"></param>
        /// <param name="ht"></param>
        /// <param name="fieldHead"></param>
        /// <returns></returns>
        public static int ContainsDTO(DataRow dr, Hashtable ht, string fieldHead)
        {
            int index = -1;
            string fstr = fieldHead + "_id";
            if (ht.ContainsKey(fstr))
            {
                index = Convert.ToInt32(ht[fstr]);
                object obj = dr[index];
                if ((obj == null) || (obj == DBNull.Value))
                {
                    index = -1;
                }
            }
            return index;
        }
        #endregion
    }
}
